package com.cf.apiInstance.aop;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.Enumeration;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.cf.apiInstance.common.bean.ApiResponse;
import com.cf.apiInstance.common.enums.ErrorCodeEnum;
import com.cf.apiInstance.common.utils.IPUtil;
import com.cf.apiInstance.domain.ApiLog;
import com.cf.apiInstance.exception.ApiRuntimeException;
import com.cf.apiInstance.service.LogService;

import lombok.extern.slf4j.Slf4j;

/**
 * 记录用户请求日志
 * 
 * @author: cf
 * @date: 28 Nov 2018 5:18:51 pm
 * @vesion: 1.0
 */
@Aspect
@Component
@Order(1)
@Slf4j
public class ApiAspect {

	@Resource
	private LogService logService;

	@Pointcut(value = "execution(* com.huashao.apiInstance.rest.*.*(..))")
	public void apiAspect() {
	}

	@Around("apiAspect()")
	public Object Around(ProceedingJoinPoint pjp) throws InstantiationException, IllegalAccessException {
		// 统一返回
		ApiResponse<?> response = ApiResponse.ok(ErrorCodeEnum.RESULT_SUCCESS.getMsg());
		HttpServletRequest request = getRequest();
		// 验证token
		String token = request.getHeader("token");
		long cost = 0;
		// token验证
		if (vailToken(token)) {
			// 定时器
			StopWatch watch = new StopWatch();
			watch.start();
			try {
				response = (ApiResponse<?>) pjp.proceed(pjp.getArgs());
			} catch (Throwable e) {
				log.error("出现错误：", e.getMessage(), e);
				Signature signature = pjp.getSignature();
				Class<?> returnType = ((MethodSignature) signature).getReturnType();
				Object object = returnType.newInstance();
				if (object instanceof ApiResponse) {
					response = (ApiResponse<?>) object;
					if (e instanceof ApiRuntimeException) {
						ApiRuntimeException apiException = (ApiRuntimeException) e;
						response = ApiResponse.error(apiException.getExceptionEnums().getCode(), apiException.getExceptionEnums().getMsg());
					} else {
						response = ApiResponse.error(ErrorCodeEnum.SYSTEM_ERROR.getCode(), ErrorCodeEnum.SYSTEM_ERROR.getMsg());
					}
				} else {
					response = ApiResponse.error(ErrorCodeEnum.BUSINESS_FAIL.getCode(), ErrorCodeEnum.BUSINESS_FAIL.getMsg());
				}
			}
			watch.stop();
			cost = watch.getTotalTimeMillis();
		} else {
			// token非法
			response = ApiResponse.error(ErrorCodeEnum.INVALID_USER.getCode(), ErrorCodeEnum.INVALID_USER.getMsg());
		}
		// 记录访问日志
		String ip = IPUtil.getClientIpAddr(request);
		String optUrl = request.getRequestURL().toString();
		String optComment = getMethodDescription(pjp);
		saveApiLog(ip, token, cost, optUrl, optComment, response.getCode(), response.getMsg());
		return response;
	}

	private HttpServletRequest getRequest() {
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		return attributes.getRequest();
	}

	private void saveApiLog(String ip, String token, long cost, String optUrl, String optComment, Integer code, String msg) {
		ApiLog log = new ApiLog(ip, token, (int)cost, optUrl, optComment, code, msg, new Date());
		logService.saveLog(log);
	}

	/**
	 * token验证
	 * 
	 * @param token
	 * @return 返回类型 boolean
	 */
	private boolean vailToken(String token) {
		// TODO Auto-generated method stub
		return true;
	}

	/**
	 * 获取方法注释说明
	 * 
	 * @param joinPoint
	 * @return 返回类型 String
	 */
	private String getMethodDescription(ProceedingJoinPoint joinPoint) {
		String description = null;
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		Method method = signature.getMethod();
		com.cf.apiInstance.annotation.ApiLog anLog = method.getAnnotation(com.cf.apiInstance.annotation.ApiLog.class);
		if (anLog != null) {
			description = anLog.description();
		}
		return description;
	}
}
